#include "jw.h"
#include <map>
#include <set>
#include <unordered_map>

void TupleSense::getAllpossibleTuples(string sensetable_name) {
	int sense_index = -1;
	for (int i = 0; i < all_tables.size(); i++) {
		if (all_tables[i].table_name == sensetable_name) {
			sense_index = i;
			break;
		}
	}
	if (sense_index == -1) {
		cout << "[Error   ] no this sense table : " << sensetable_name << endl;
		exit(0);
	}
	
	//cout << "sense_index " << sense_index << endl;

	map<string, set<string>> col_values;
	set<string> sense_cols;
	for (int i = 0; i < all_tables[sense_index].column_names.size(); i++) {
		sense_cols.insert(all_tables[sense_index].column_names[i]);
	}

	for (int i = 0; i < all_tables.size(); i++) {
		if (i != sense_index) {
			//cout << "i :" << i << endl;
			for (int j = 0; j < all_tables[i].column_names.size(); j++) {
				string colname = all_tables[i].column_names[j];
				if (sense_cols.count(colname)) {
					//cout << "colname " << colname << endl;
					if (col_values.count(colname) == 0) {
						col_values[colname] = set<string>();
					}
					int row = all_tables[i].values[0].size();
					for (int k = 0; k < row; k++) {
						//cout << "value : " << all_tables[i].values[j][k] << endl;
						col_values[colname].insert(all_tables[i].values[j][k]);
					}
				}
			}
		}
	}

	for (auto& p : col_values) {
		cout << "colname : " << p.first << endl;
		for (auto& s : p.second) {
			cout << s << endl;
		}
	}

}

void TupleSense::computeTupleSenseGroup(Table& temp_table, string groupcol, string aggcol, Table& res_table) {
	vector<int> sensecol_index;
	
	for (int i = 0; i < sense_tables.size(); i++) {
		string tbit_col = sense_tables[i] + "_tbit";
		int tbit_index = -1;
		for (int j = 0; j < temp_table.column_names.size(); j++) {
			if (temp_table.column_names[j] == tbit_col) {
				tbit_index = j;
			}
		}
		if (tbit_index == -1) {
			cout << "[Error   ] not find tbit :" << tbit_col << endl;
			exit(0);
		}
		sensecol_index.push_back(tbit_index);
	}

	// < sensetable_tbit : < group_value, sense_value > >
	vector<unordered_map<string, unordered_map<string, double>>> sense_res;
	for (int i = 0; i < sensecol_index.size(); i++) {
		sense_res.push_back(unordered_map<string, unordered_map<string, double>>());
	}

	int group_index = -1;
	int agg_index = -1;

	for (int i = 0; i < temp_table.column_names.size(); i++) {
		if (temp_table.column_names[i] == aggcol) {
			agg_index = i;
		}
		else if (temp_table.column_names[i] == groupcol) {
			group_index = i;
		}
	}
	if (group_index == -1) {
		cout << "[Error   ] not find group col :" << groupcol << endl;
		exit(0);
	}
	if (agg_index == -1) {
		cout << "[Error   ] not find agg col :" << aggcol << endl;
		exit(0);
	}

	int row_num = temp_table.values[0].size();
	for (int i = 0; i < row_num; i++) {
		for (int k = 0; k < sensecol_index.size(); k++) {
			string tbit = temp_table.values[sensecol_index[k]][i];
			string group = temp_table.values[group_index][i];
			double agg = stod(temp_table.values[agg_index][i]);
			if (sense_res[k].count(tbit) == 0) {
				sense_res[k][tbit] = unordered_map<string, double>();
			}
			if (sense_res[k][tbit].count(group) == 0) {
				sense_res[k][tbit][group] = 0;
			}
			sense_res[k][tbit][group] += agg;
		}
	}

	int res_row = 0;
	for (int i = 0; i < sensecol_index.size(); i++) {
		for (auto& p1 : sense_res[i]) {
			res_row += p1.second.size();
		}
	}
	
	cout << "[Result  ]totally rows : " << res_row << endl;
	
	int show_row = min(30, res_row);
	int counter = 0;
	for (int i = 0; i < sensecol_index.size(); i++) {
		int table_id = -1;
		for (int k = 0; k < all_tables.size(); k++) {
			if (all_tables[k].table_name == sense_tables[i]) {
				table_id = k;
			}
		}
		if (table_id == -1) {
			cout << "[Error   ] not find " << sense_tables[i] << endl;
			exit(0);
		}

		for (auto& p1 : sense_res[i]) {
			for (auto& p2 : p1.second) {
				counter++;
				int tbit = stoi(p1.first);
				cout << sense_tables[i] << "|";
				for (int j = 0; j < all_tables[table_id].column_names.size(); j++) {
					cout << all_tables[table_id].values[j][tbit] << "|";
				}

				cout << p2.first << "|" << p2.second << endl;
				if (counter > show_row) {
					return;
				}
			}
		}
	}


}

void TupleSense::computeTupleSense(Table& temp_table, string aggcol, Table& res_table) {
	vector<int> sensecol_index;

	for (int i = 0; i < sense_tables.size(); i++) {
		string tbit_col = sense_tables[i] + "_tbit";
		int tbit_index = -1;
		for (int j = 0; j < temp_table.column_names.size(); j++) {
			if (temp_table.column_names[j] == tbit_col) {
				tbit_index = j;
			}
		}
		if (tbit_index == -1) {
			cout << "[Error   ] not find tbit :" << tbit_col << endl;
			exit(0);
		}
		sensecol_index.push_back(tbit_index);
	}

	// < sensetable_tbit : sense_value > >
	vector<unordered_map<string, double>> sense_res;
	for (int i = 0; i < sensecol_index.size(); i++) {
		sense_res.push_back(unordered_map<string, double>());
	}

	int agg_index = -1;
	for (int i = 0; i < temp_table.column_names.size(); i++) {
		if (temp_table.column_names[i] == aggcol) {
			agg_index = i;
		}
	}
	
	if (agg_index == -1) {
		cout << "[Error   ] not find agg col :" << aggcol << endl;
		exit(0);
	}

	int row_num = temp_table.values[0].size();
	for (int i = 0; i < row_num; i++) {
		for (int k = 0; k < sensecol_index.size(); k++) {
			string tbit = temp_table.values[sensecol_index[k]][i];
			double agg = stod(temp_table.values[agg_index][i]);
			if (sense_res[k].count(tbit) == 0) {
				sense_res[k][tbit] = 0;
			}
			sense_res[k][tbit] += agg;
		}
	}

	int res_row = 0;
	for (int i = 0; i < sensecol_index.size(); i++) {
		res_row += sense_res[i].size();
	}

	cout << "[Result  ]totally rows : " << res_row << endl;

	int show_row = min(30, res_row);
	int counter = 0;
	for (int i = 0; i < sensecol_index.size(); i++) {
		int table_id = -1;
		for (int k = 0; k < all_tables.size(); k++) {
			if (all_tables[k].table_name == sense_tables[i]) {
				table_id = k;
			}
		}
		if (table_id == -1) {
			cout << "[Error   ] not find " << sense_tables[i] << endl;
			exit(0);
		}

		for (auto& p1 : sense_res[i]) {
			counter++;
			int tbit = stoi(p1.first);
			cout << sense_tables[i] << "|";
			for (int j = 0; j < all_tables[table_id].column_names.size(); j++) {
				cout << all_tables[table_id].column_names[j] << "=" << all_tables[table_id].values[j][tbit] << "|";
			}
			cout << p1.first << "|" << p1.second << endl;
			if (counter > show_row) {
				return;
			}
		}
	}


}

void TupleSense::computeTupleSenseCount(Table& temp_table, Table& res_table) {
	vector<int> sensecol_index;

	for (int i = 0; i < sense_tables.size(); i++) {
		string tbit_col = sense_tables[i] + "_tbit";
		int tbit_index = -1;
		for (int j = 0; j < temp_table.column_names.size(); j++) {
			if (temp_table.column_names[j] == tbit_col) {
				tbit_index = j;
			}
		}
		if (tbit_index == -1) {
			cout << "[Error   ] not find tbit :" << tbit_col << endl;
			exit(0);
		}
		sensecol_index.push_back(tbit_index);
	}

	// < sensetable_tbit : sense_value > >
	vector<unordered_map<string, double>> sense_res;
	for (int i = 0; i < sensecol_index.size(); i++) {
		sense_res.push_back(unordered_map<string, double>());
	}

	int row_num = temp_table.values[0].size();
	for (int i = 0; i < row_num; i++) {
		for (int k = 0; k < sensecol_index.size(); k++) {
			string tbit = temp_table.values[sensecol_index[k]][i];
			if (sense_res[k].count(tbit) == 0) {
				sense_res[k][tbit] = 0;
			}
			sense_res[k][tbit] += 1.0;
		}
	}

	int res_row = 0;
	for (int i = 0; i < sensecol_index.size(); i++) {
		res_row += sense_res[i].size();
	}

	cout << "[Result  ]totally rows : " << res_row << endl;

	int show_row = min(30, res_row);
	int counter = 0;
	for (int i = 0; i < sensecol_index.size(); i++) {
		int table_id = -1;
		for (int k = 0; k < all_tables.size(); k++) {
			if (all_tables[k].table_name == sense_tables[i]) {
				table_id = k;
			}
		}
		if (table_id == -1) {
			cout << "[Error   ] not find " << sense_tables[i] << endl;
			exit(0);
		}

		for (auto& p1 : sense_res[i]) {
			counter++;
			int tbit = stoi(p1.first);
			cout << sense_tables[i] << "|";
			for (int j = 0; j < all_tables[table_id].column_names.size(); j++) {
				cout << all_tables[table_id].column_names[j] << "=" << all_tables[table_id].values[j][tbit] << "|";
			}
			cout << p1.first << "|" << p1.second << endl;
			if (counter > show_row) {
				return;
			}
		}
	}


}

void TupleSense::computeTupleSenseMax(Table& temp_table, string groupcol, string aggcol) {
	string tbit_col = sense_tables[0] + "_tbit";
	int tbit_index = -1;
	
	for (int j = 0; j < temp_table.column_names.size(); j++) {
		if (temp_table.column_names[j] == tbit_col) {
			tbit_index = j;
		}
	}
	if (tbit_index == -1) {
		cout << "[Error   ] not find tbit :" << tbit_col << endl;
		exit(0);
	}
	
	int group_index = -1;
	int agg_index = -1;

	for (int i = 0; i < temp_table.column_names.size(); i++) {
		if (temp_table.column_names[i] == aggcol) {
			agg_index = i;
		}
		else if (temp_table.column_names[i] == groupcol) {
			group_index = i;
		}
	}
	if (group_index == -1) {
		cout << "[Error   ] not find group col :" << groupcol << endl;
		exit(0);
	}
	if (agg_index == -1) {
		cout << "[Error   ] not find agg col :" << aggcol << endl;
		exit(0);
	}



	// < sensetable_tbit : sense_value > >
	unordered_map<string, double> group_max;
	unordered_map<string, double> sense_res;
	

	int row_num = temp_table.values[0].size();
	for (int i = 0; i < row_num; i++) {
		string group = temp_table.values[group_index][i];
		if (group_max.count(group) == 0) {
			group_max[group] = 0;
		}
		group_max[group] = max(group_max[group], stod(temp_table.values[agg_index][i]));
	}
	
	for (int i = 0; i < row_num; i++) {
		string group = temp_table.values[group_index][i];
		string tbit = temp_table.values[tbit_index][i];

		if (sense_res.count(tbit) == 0) {
			sense_res[tbit] = 0;
		}
		
		if (stod(temp_table.values[agg_index][i]) >= group_max[group]) {
			sense_res[tbit] = 1;
		}

	}

	int res_row = sense_res.size();
	

	cout << "[Result  ]totally rows : " << res_row << endl;

	int show_row = min(30, res_row);
	int counter = 0;
	
	int table_id = -1;
	for (int k = 0; k < all_tables.size(); k++) {
		if (all_tables[k].table_name == sense_tables[0]) {
			table_id = k;
		}
	}
	if (table_id == -1) {
		cout << "[Error   ] not find " << sense_tables[0] << endl;
		exit(0);
	}

	for (auto& p1 : sense_res) {
		counter++;
		int tbit = stoi(p1.first);
		cout << sense_tables[0] << "|";
		for (int j = 0; j < all_tables[table_id].column_names.size(); j++) {
			cout << all_tables[table_id].column_names[j] << "=" << all_tables[table_id].values[j][tbit] << "|";
		}
		cout << p1.first << "|" << p1.second << endl;
		if (counter > show_row) {
			return;
		}
	}
	
}